#ifndef CPU_HPP
#define CPU_HPP

#include <QObject>
#include <QMutex>
#include <QMutexLocker>

#include "command.hpp"
#include "watchdog.hpp"
#include "datamemoryorganisation.hpp"
#include "stackmanagement.hpp"

class DataMemoryOrganisation;
class Bank;
class IORegister;
class Watchdog;

/**
* @brief Class CPU represents the "ALU" of the PIC
* @details This class is used to seperate the "real" execution of the Command. \n
* The executing Thread calls this class' execute function for every Command he wants to execute. \n
* The corresponding Registers will be changed and the new PC will be returned.
* @see int executeCommand(Command *command, int programCounter)
*
* @class CPU cpu.hpp "cpu.hpp"
*/
class CPU : public QObject
{
Q_OBJECT
public:

    /**
    * @brief Constructor for CPU
    *
    * @fn explicit CPU(DataMemoryOrganisation *dmo, StackManagement *sm, Watchdog *watchdog, QObject *parent = 0)
    * @param dmo
    * @param sm
    * @param watchdog
    * @param parent
    */
    explicit CPU(DataMemoryOrganisation *dmo, StackManagement *sm, Watchdog *watchdog, QObject *parent = 0);

    /**
    * @brief Copy Constructor
    *
    * @fn CPU(const CPU &other)
    * @param other
    */
    CPU(const CPU &other);

    /**
    * @brief Destructor for CPU
    *
    * @fn ~CPU
    */
    ~CPU();

    /**
    * @brief Executes the given command.
    * @see int Command_ADDWF(int argumentDirBit, int argument7Bit) const
    * @see int Command_ANDWF(int argumentDirBit, int argument7Bit) const
    * @see int Command_CLRF(int argument7Bit) const
    * @see int Command_CLRW() const
    * @see int Command_COMF(int argumentDirBit, int argument7Bit) const
    * @see int Command_DECF(int argumentDirBit, int argument7Bit) const
    * @see int Command_DECFSZ(int argumentDirBit, int argument7Bit, int *programcounter) const
    * @see int Command_INCF(int argumentDirBit, int argument7Bit) const
    * @see int Command_INCFSZ(int argumentDirBit, int argument7Bit, int *programcounter) const
    * @see int Command_IORWF(int argumentDirBit, int argument7Bit) const
    * @see int Command_MOVF(int argumentDirBit, int argument7Bit) const
    * @see int Command_MOVWF(int argument7Bit) const
    * @see int Command_NOP() const
    * @see int Command_RLF(int argumentDirBit, int argument7Bit) const
    * @see int Command_RRF(int argumentDirBit, int argument7Bit) const
    * @see int Command_SUBWF(int argumentDirBit, int argument7Bit) const
    * @see int Command_SWAPF(int argumentDirBit, int argument7Bit) const
    * @see int Command_XORWF(int argumentDirBit, int argument7Bit) const
    * @see int Command_BCF(int argument3Bit, int argument7Bit) const
    * @see int Command_BSF(int argument3Bit, int argument7Bit) const
    * @see int Command_BTFSC(int argument3Bit, int argument7Bit, int *programcounter) const
    * @see int Command_BTFSS(int argument3Bit, int argument7Bit, int *programcounter) const
    * @see int Command_ADDLW(int argument8Bit) const
    * @see int Command_ANDLW(int argument8Bit) const
    * @see int Command_CALL(int argument11Bit, int *programcounter) const
    * @see int Command_CLRWDT() const
    * @see int Command_GOTO(int argument11Bit, int *programcounter) const
    * @see int Command_IORLW(int argument8Bit) const
    * @see int Command_MOVLW(int argument8Bit) const
    * @see int Command_RETFIE(int *programcounter) const
    * @see int Command_RETLW(int argument8Bit, int *programcounter) const
    * @see int Command_RETURN(int *programcounter) const
    * @see int Command_SLEEP() const
    * @see int Command_SUBLW(int argument8Bit) const
    * @see int Command_XORLW(int argument8Bit) const
    *
    * @fn int executeCommand(Command *command, int programCounter)
    * @param command
    * @param programCounter
    */
    int executeCommand(Command *command, int programCounter);

    /**
    * @brief Resets the runtime
    *
    * @fn void reset()
    */
    void reset();

    /**
    * @brief Returns the Runtime as String
    *
    * @fn QString getRuntimeAsString() const
    * @return int
    */
    QString getRuntimeAsString() const;

    /**
    * @brief Returns the Watchdog as pointer
    *
    * @fn Watchdog *getWatchdog() const
    * @return Watchdog
    */
    Watchdog *getWatchdog() const;

    /**
    * @brief Returns the DataMemoryOrganisation as pointer
    *
    * @fn DataMemoryOrganisation *getDataMemoryOrganisation() const
    * @return DataMemoryOrganisation
    */
    DataMemoryOrganisation *getDataMemoryOrganisation() const;

    /**
    * @brief Returns the StackManagement as pointer
    *
    * @fn StackManagement *getStackManagement() const
    * @return StackManagement
    */
    StackManagement *getStackManagement() const;

    // All functions

    /**
    * @brief Add working-register and file-register
    * @details The working-register content is added to the content of the given register.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Carry-Flag, DigitCarry-Flag and Zero-Flag
    * @see void setCarryFlag(int newValue) const
    * @see void setDigitCarryFlag(int oldValue, int newValue) const
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_ADDWF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_ADDWF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief AND working-register with file-register
    * @details The working-register content is anded with the content of the given register.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_ANDWF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_ANDWF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Clear file-register
    * @details The given register is cleared.
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_CLRF(int argument7Bit) const
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_CLRF(int argument7Bit) const;

    /**
    * @brief Clear working-register
    * @details The working-register is cleared.
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_CLRW() const
    * @return cycles: 1
    */
    int Command_CLRW() const;

    /**
    * @brief Complement file-register
    * @details The content of the given register is complemented.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Zero
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_COMF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_COMF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Decrement file-register
    * @details Decrements the content of the given register.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_DECF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_DECF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Decrement file-register, skip if zero
    * @details Decrements the content of the given register. If the result is 0 the next instruction is skipped and instead of the instruction a nop is executed.
    * Else the next instruction is executed.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: None
    *
    * @fn int Command_DECFSZ(int argumentDirBit, int argument7Bit, int *programcounter) const
    * @param argumentDirBit
    * @param argument7Bit
    * @param programcounter
    * @return cycles: 1
    */
    int Command_DECFSZ(int argumentDirBit, int argument7Bit, int *programcounter) const;

    /**
    * @brief Increment file-register
    * @details Increments the content of the given register.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_INCF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_INCF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Increment file-register, skip if zero
    * @details Increments the content of the given register. If the result is 0 the next instruction is skipped and instead of the instruction a nop is executed.
    * Else the next instruction is executed.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: None
    *
    * @fn int Command_INCFSZ(int argumentDirBit, int argument7Bit, int *programcounter) const
    * @param argumentDirBit
    * @param argument7Bit
    * @param programcounter
    * @return cycles: 1, 2 if nop is executed
    */
    int Command_INCFSZ(int argumentDirBit, int argument7Bit, int *programcounter) const;

    /**
    * @brief Inclusive OR working-register with file-register
    * @details The working-register content is ored with the content of the given register.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_IORWF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_IORWF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Move file-register
    * @details The content of the given register is moved.
    * If the d-bit is set, the data is stored in the given register. This is useful to test the register, because the zero-flag is affected.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_MOVF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_MOVF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Move working-register to file-register
    * @details Move the working-register content to the given register. \n
    * Affected Flags: None
    *
    * @fn int Command_MOVWF(int argument7Bit) const
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_MOVWF(int argument7Bit) const;

    /**
    * @brief No operation
    * @details No operation is executed. The program counter is incremented. \n
    * Affected Flags: None
    *
    * @fn int Command_NOP() const
    * @return int
    */
    int Command_NOP() const;

    /**
    * @brief Rotate left file-register through carry
    * @details Rotates the content of the given register 1 bit to the left through the carry-flag. \n
    * The carry-flag is rotated into the lowest bit of the output register. The highest bit of the register ist rotated into the carry.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Carry-Flag
    * @see void setCarryFlag(int newValue) const
    *
    * @fn int Command_RLF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_RLF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Rotate right file-register through carry
    * @details Rotates the content of the given register 1 bit to the right through the carry-flag.
    * The carry-flag is rotated into the highest bit of the output register. The lowest bit of the register is rotated into the carry.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Carry-Flag
    * @see void setCarryFlag(int newValue) const
    *
    * @fn int Command_RRF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_RRF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Subtract working-register from file-register
    * @details  The working-register content is subtracted from the given register.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Carry-Flag, DigitCarry-Flag and Zero-Flag
    * @see void setCarryFlag(int newValue) const
    * @see void setDigitCarryFlag(int oldValue, int newValue) const
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_SUBWF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_SUBWF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Swap nibbles in file-register
    * @details The lower and upper 4 bits of the given register are swapped.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: None
    *
    * @fn int Command_SWAPF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return int
    */
    int Command_SWAPF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Exclusive OR working-register with file-register
    * @details The working-register content is xored with the content of the given register.
    * If the d-bit is set, the result is stored in the given register.
    * Else the result is stored in the working-register. \n
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_XORWF(int argumentDirBit, int argument7Bit) const
    * @param argumentDirBit
    * @param argument7Bit
    * @return int
    */
    int Command_XORWF(int argumentDirBit, int argument7Bit) const;

    /**
    * @brief Bit clear file-register
    * @details The given bit in the given register is cleared. \n
    * Affected Flags: None
    *
    * @fn int Command_BCF(int argument3Bit, int argument7Bit) const
    * @param argument3Bit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_BCF(int argument3Bit, int argument7Bit) const;

    /**
    * @brief Bit set file-register
    * @details The given bit in the given register is set. \n
    * Affected Flags: None
    *
    * @fn int Command_BSF(int argument3Bit, int argument7Bit) const
    * @param argument3Bit
    * @param argument7Bit
    * @return cycles: 1
    */
    int Command_BSF(int argument3Bit, int argument7Bit) const;

    /**
    * @brief Bit test file-register, skip if clear
    * @details If the given bit of the given register is clear, the next instruction is skipped and instead of the instruction a nop is executed.
    * Else the next instruction is executed. \n
    * Affected Flags: None
    *
    * @fn int Command_BTFSC(int argument3Bit, int argument7Bit, int *programcounter) const
    * @param argument3Bit
    * @param argument7Bit
    * @param programcounter
    * @return cycles: 1, 2 if nop is executed
    */
    int Command_BTFSC(int argument3Bit, int argument7Bit, int *programcounter) const;

    /**
    * @brief Bit test file-register, skip if set
    * @details If the given bit of the given register is set, the next instruction is skipped and instead of the inctruction a nop is executed.
    * Else the next instruction is executed. \n
    * Affected Flags: None
    *
    * @fn int Command_BTFSS(int argument3Bit, int argument7Bit, int *programcounter) const
    * @param argument3Bit
    * @param argument7Bit
    * @param programcounter
    * @return cycles: 1, 2 if nop is executed
    */
    int Command_BTFSS(int argument3Bit, int argument7Bit, int *programcounter) const;

    /**
    * @brief Add literal and working-register
    * @details The given 8-bit literal is added to the working-register.
    * The result is stored in the working-register. \n
    * Affected Flags: Carry-Flag, DigitCarry-Flag and Zero-Flag
    * @see void setCarryFlag(int newValue) const
    * @see void setDigitCarryFlag(int oldValue, int newValue) const
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_ADDLW(int argument8Bit) const
    * @param argument8Bit
    * @return cycles: 1
    */
    int Command_ADDLW(int argument8Bit) const;

    /**
    * @brief AND literal with working-register
    * @details The working-register content is anded with the given 8-bit literal.
    * The result is placed in the working-register. \n
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_ANDLW(int argument8Bit) const
    * @param argument8Bit
    * @return cycles: 1
    */
    int Command_ANDLW(int argument8Bit) const;

    /**
    * @brief Call subroutine
    * @details  Program counter + 1 is pushed on the stack. The given 11-bit literal is loaded in the program counter.\n
    * Affected Flags: None
    *
    * @fn int Command_CALL(int argument11Bit, int *programcounter) const
    * @param argument11Bit
    * @param programcounter
    * @return cycles: 2
    */
    int Command_CALL(int argument11Bit, int *programcounter) const;

    /**
    * @brief Clear Watchdog Timer
    * @details Resets the watchdogtimer and the prescaler.
    *
    * @fn int Command_CLRWDT() const
    * @return cycles: 1
    */
    int Command_CLRWDT() const;

    /**
    * @brief Jump to address
    * @details The given 11-bit literal is loaded in the program counter.  \n
    * Affected Flags: None
    *
    * @fn int Command_GOTO(int argument11Bit, int *programcounter) const
    * @param argument11Bit
    * @param programcounter
    * @return cycles: 2
    */
    int Command_GOTO(int argument11Bit, int *programcounter) const;

    /**
    * @brief Inclusive OR literal with working-register
    * @details The working-register content is ored with the given 8-bit literal.
    * The result is stored in the working-register. \n
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_IORLW(int argument8Bit) const
    * @param argument8Bit
    * @return cycles: 1
    */
    int Command_IORLW(int argument8Bit) const;

    /**
    * @brief Move literal to working-register
    * @details The given 8-bit literal is loaded in the working-register. \n
    * Affected Flags: None
    *
    * @fn int Command_MOVLW(int argument8Bit) const
    * @param argument8Bit
    * @return cycles: 1
    */
    int Command_MOVLW(int argument8Bit) const;

    /**
    * @brief Return with Literal in working-register
    * @details The top of the stack is loaded in the program counter and is popped from the stack.
    * The global-interrupt-enable-bit is set back to one. \n
    * Affected Flags: None
    *
    * @fn int Command_RETFIE(int *programcounter) const
    * @param programcounter
    * @return cycles: 2
    */
    int Command_RETFIE(int *programcounter) const;

    /**
    * @brief Return with literal in working-register
    * @details The top of the stack is loaded in the program counter and is popped from the stack.
    * The given literal is loaded in the working-register. \n
    * Affected Flags: None
    *
    * @fn int Command_RETLW(int argument8Bit, int *programcounter) const
    * @param argument8Bit
    * @param programcounter
    * @return cycles: 2
    */
    int Command_RETLW(int argument8Bit, int *programcounter) const;

    /**
    * @brief Return from subroutine
    * @details The top of the stack is loaded in the program counter and is popped from the stack. \n
    * Affected Flags: None
    *
    * @fn int Command_RETURN(int *programcounter) const
    * @param programcounter
    * @return cycles: 2
    */
    int Command_RETURN(int *programcounter) const;

    /**
    * @brief Increments the runtime.
    * @details
    *
    * @fn int Command_SLEEP() const
    * @return cycles: 1
    */
    int Command_SLEEP() const;

    /**
    * @brief Subtract working-register from Literal
    * @details The working-register content is subtracted from the given 8-bit literal.
    * The result is stored in the working-register. \n
    * Affected Flags: Carry-Flag, DigitCarry-Flag and Zero-Flag
    * @see void setCarryFlag(int newValue) const
    * @see void setDigitCarryFlag(int oldValue, int newValue) const
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_SUBLW(int argument8Bit) const
    * @param argument8Bit
    * @return cycles: 1
    */
    int Command_SUBLW(int argument8Bit) const;

    /**
    * @brief Exclusive OR Literal with working-register
    * @details The working-register content is xored with the given 8-bit literal.
    * The result is stored in the working-register. \n
    * Affected Flags: Zero-Flag
    * @see void setZeroFlag(int newValue) const
    *
    * @fn int Command_XORLW(int argument8Bit) const
    * @param argument8Bit
    * @return cycles: 1
    */
    int Command_XORLW(int argument8Bit) const;

private:

    Watchdog *watchdog;
    DataMemoryOrganisation *dataMemoryOrganisation;
    StackManagement *stackManagement;
    int runtime;
    QMutex *mutex;

    /**
    * @brief Sets the Carry-Flag
    * @details If the newValue is greater 255, the Carry-flag is set.
    * Else the flag is cleared.
    *
    * @fn void setCarryFlag(int newValue) const
    * @param newValue
    */
    void setCarryFlag(int newValue) const;

    /**
    * @brief Sets the DigitCarry-Flag
    * @details If the oldValue is below 16 and the newValue is greater or equal 16, the DigitCarry-flag is set.
    * Else the flag is cleared.
    *
    * @fn void setDigitCarryFlag(int oldValue, int newValue) const
    * @param oldValue
    * @param newValue
    */
    void setDigitCarryFlag(int oldValue, int newValue) const;

    /**
    * @brief Sets the Zero-Flag
    * @details If the newValue is 0, the Zero-flag is set.
    * Else the flag is cleared.
    *
    * @fn void setZeroFlag(int newValue) const
    * @param newValue
    */
    void setZeroFlag(int newValue) const;

signals:

    /**
    * @brief Emitted on interrupt
    *
    * @fn void interrupt()
    */
    void interrupt();

};

#endif // CPU_HPP
